/*
   Copyright (C) 2011 Roman Belov <romblv@gmail.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>

#include "lib/base.h"
#include "lib/io.h"
#include "lang/parser.h"
#include "lang/signal.h"
#include "sim/args.h"
#include "sim/edsim.h"

#define MEM_64K_8_SIZE			(65536)

struct lib_mem_64k_8_cust {
	const char		*dumpfile;	/* dump file */
	uint8_t			*mem;
};

int
lib_mem_64k_8(struct module *m)
{
	int				arg[26];
	struct lib_mem_64k_8_cust	*cust = m->cust;
	struct spec			*s;
	FILE				*fd;
	int				in_addr;
	int				inout_data;
	int				in_en, in_cs;
	int				touch;
	int				ret, i;

	if (cust == NULL) {

		cust = malloc(sizeof(struct lib_mem_64k_8_cust));

		cust->mem = malloc(MEM_64K_8_SIZE);
		memset(cust->mem, 0, MEM_64K_8_SIZE);

		s = spec_find(m, "dumpfile");
		if (s && s->type == SPEC_STRING) cust->dumpfile = s->val.s;
		else edsim_lib_share_spec_fail_report(m, s);

		m->cust = cust;

		/* Try to restore memory contents from dump file.
		 * */

		do {
			if (strlen(cust->dumpfile) == 0)
				break;

			fd = fopen(cust->dumpfile, "rb");

			if (fd == NULL) {
				fprintf(stderr, "runtime: the \"%s/%s\" module cannot open dump file"
						" \"%s\" : \"%s\" \n",
						m->id, m->lid, s ? s->id : "NULL",
						strerror(errno));
				exit(EXIT_FAILURE);
			}

			ret = fread(cust->mem, MEM_64K_8_SIZE, 1, fd);

			if (ret < MEM_64K_8_SIZE) {

				/* File too short but it does not make sense.
				 * */
			}

			fclose(fd);
		}
		while (0);
	}

	edsim_lib_share_in(m, arg, 26);

	in_addr = 0;
	inout_data = 0;
	in_en = arg[24];
	in_cs = arg[25];

	for (i = 0; i < 16; ++i) {

		in_addr |= arg[i] ? (1 << i) : 0 ;
	}

	for (i = 0; i < 8; ++i) {

		inout_data |= arg[i + 16] ? (1 << i) : 0 ;
	}

	if (in_en && in_cs) {
		fprintf(stderr, "runtime: wrong usage of the \"%s/%s\" module,"
				" \"en\" and \"cs\" pins should not be pulled up together\n",
				m->id, m->lid);
		exit(EXIT_FAILURE);
	}

	touch = 0;

	if (in_en) {

		inout_data = cust->mem[in_addr];

		for (i = 0; i < 8; ++i) {

			ret = (inout_data >> i) & 1;
			touch += edsim_lib_share_out(m, i + 16, ret);
		}
	}
	else {
		for (i = 0; i < 8; ++i) {

			touch += edsim_lib_share_unpull(m, i + 16);
		}
	}

	if (in_cs) {

		cust->mem[in_addr] = inout_data;
	}

	return touch;
}


void *
lib_mem_get_cacl(const char *lib)
{
	if (!strcmp(lib, "mem_64k_8"))
		return lib_mem_64k_8;
	else
		return NULL;
}

